import { defineEventHandler, readBody, createError } from 'h3'
import type { H3Event } from 'h3'
import redis from '../util/redis'

const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
function randomKey(len = 8) {
  let out = ''
  for (let i = 0; i < len; i++) out += ALPHABET[Math.floor(Math.random() * ALPHABET.length)]
  return out
}

export default defineEventHandler(async (event: H3Event) => {
  const body = await readBody(event) as any
  const url = (body && body.url) || body
  // expireSeconds: number of seconds the shortlink should live; null/0 means never expire
  const expireSecondsRaw = body && Object.prototype.hasOwnProperty.call(body, 'expireSeconds') ? body.expireSeconds : undefined
  let expireSeconds: number | null = null
  if (expireSecondsRaw === undefined || expireSecondsRaw === null) {
    expireSeconds = null
  } else {
    expireSeconds = Number(expireSecondsRaw) || 0
    if (expireSeconds <= 0) expireSeconds = null
  }
  if (!url || typeof url !== 'string') {
    throw createError({ statusCode: 400, statusMessage: 'invalid url' })
  }
  if (!/^https?:\/\//i.test(url)) {
    throw createError({ statusCode: 400, statusMessage: 'url must start with http:// or https://' })
  }

  const client: any = redis
  const prefix = 'shortlink:'

  // Try generate unique key
  for (let i = 0; i < 6; i++) {
    const key = randomKey(8)
    const fullKey = prefix + key
    // Use SETNX: set if not exists
    const set = await client.setnx(fullKey, url)
    if (set === 1) {
      // set TTL when expireSeconds is provided
      if (expireSeconds && expireSeconds > 0) {
        try {
          await client.expire(fullKey, expireSeconds)
        } catch (e) {
          // ignore expire error but log if needed
        }
      }
      const host = event.node.req.headers.host || ''
      const proto = event.node.req.headers['x-forwarded-proto'] || 'https'
      const shortUrl = `${proto}://${host}/s/${key}`
      const resp: any = { key, shortUrl }
      if (expireSeconds && expireSeconds > 0) {
        resp.expireAt = Date.now() + expireSeconds * 1000
      } else {
        resp.expireAt = null
      }
      return resp
    }
  }

  throw createError({ statusCode: 500, statusMessage: 'could not generate unique key' })
})
